package com.txw.javaCore.server.webSocket;

import com.google.gson.Gson;
import com.txw.javaCore.api.response.BaseResponse;
import com.txw.javaCore.api.response.StatusCode;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @PROJECT_NAME: JavaCoreB2
 * @DESCRIPTION: 首页通知消息Server层：后端webSocket实例-考虑下并发安全性问题
 * @Author: 涂玄武
 * @DATE: 2021/3/5 14:33
 */
@ServerEndpoint("/wx/index/{userId}")
@Service
@Slf4j
public class WebSocketIndexMsgServer {

    private static final Gson GSON = new Gson();

    /**
     * juc包下的原子操作类:用来统计前端当前在线连接数
     */
    private static AtomicInteger onlineCount = new AtomicInteger(0);

    /**
     * 保证线程安全：一个用户userId对应一个后端连接实例WebSocket对象
     */
    private static ConcurrentHashMap<String, WebSocketIndexMsgServer> wsMap = new ConcurrentHashMap<>();

    /**
     * 与某个用户建立起连接时的会话/channel，后端是通过session/channel将数据传输给到前端
     */
    private Session session;

    /**
     * 具体的某个用户
     */
    private String userId;

    /**
     * 建立连接成功时调用的方法
     *
     * @param session
     * @param userId
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("userId") String userId) {
        this.session = session;
        this.userId = userId;

        //每次前端建立webSocket连接到后端时，都需要判断下当前用户建立的链接是否是最新的
        if (wsMap.containsKey(userId)) {
            wsMap.remove(userId);
            wsMap.put(userId, this);
        } else {
            //有一个用户连接成功缓存至Map中
            wsMap.put(userId, this);
            addOnlineCount();
        }

        //统计当前前端有多少用户建立了与后端的webSocket连接
        log.info("当前成功与后端建立了webSocket连接数：{} 当前用户ID：{}", getOnlineCount(), userId);

        //后端返回消息给到前端：你已经成功建立了连接了，可以开始通信了
        try {
            BaseResponse response = new BaseResponse(StatusCode.WebSocketClientSuccessLinkToServer);
            sendMessage(GSON.toJson(response));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 后端推送消息给前端
     *
     * @param message
     */
    @OnMessage
    public void onMessage(String message) {
        log.info("后端推送消息给到前端，当前用户ID：{}，消息为：{}", userId, message);
    }

    /**
     * 后端服务器主动推送消息给到前端
     *
     * @param message
     */
    public void sendMessage(String message) throws Exception {
        //getBasicRemote()同步发送文本消息
        this.session.getBasicRemote().sendText(message);
    }

    /**
     * 发生一些意想不到的错误
     *
     * @param e
     */
    @OnError
    public void onError(Throwable e) {
        log.error("发生一些意想不到的错误webSocket，用户ID：{}，原因：{}", userId, e.getMessage());
        e.printStackTrace();
    }

    /**
     * 连接关闭
     */
    @OnClose
    public void onClose() {
        onCloseUser(userId);
    }

    /**
     * 用户端断开与后端的webSocket连接
     *
     * @param userId
     */
    public void onCloseUser(final String userId) {
        if (wsMap.containsKey(userId)) {
            wsMap.remove(userId);
            subOnlineCount();
        }
        log.info("当前用户端断开与后端的webSocket连接，在线人数-1，userID:{}，在线总数：{}", userId, getOnlineCount());
    }

    /**
     * 在线连接/人数+1
     */
    public static void addOnlineCount() {
        onlineCount.incrementAndGet();
    }

    /**
     * 在线连接/人数-1
     */
    public static void subOnlineCount() {
        onlineCount.decrementAndGet();
    }

    /**
     * 获取当前在线连接/人数
     */
    public static int getOnlineCount() {
        return onlineCount.get();
    }

    /**
     * 发送自定义的消息给客户端-与业务数据挂钩
     *
     * @param message
     * @param userId
     * @throws Exception
     */
    public void sendIndexMsg(String message, final String userId) throws Exception {
        if (StringUtils.isNotBlank(userId) && wsMap.containsKey(userId)) {
            wsMap.get(userId).sendMessage(message);
        } else {
            log.info("当前用户不在线：{}", userId);
        }
    }

}
